"""
Output Formatter

Formats resolved type information for human-readable output with proper
demangling and pretty-printing.
"""

import json
import re
from typing import Dict, Any, List, Optional
from dataclasses import asdict
from datetime import datetime

from .type_resolver import ResolvedType
from .ast_parser import SourcePosition


class OutputFormatter:
    """Formats TIR analysis results for various output formats."""
    
    def __init__(self):
        self.demangle_rules = self._init_demangle_rules()
    
    def _init_demangle_rules(self) -> Dict[str, str]:
        """Initialize common demangling rules for cleaner output."""
        return {
            # Standard library types
            r'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>\s*>': 'std::string',
            r'std::basic_string<char, std::char_traits<char>, std::allocator<char>\s*>': 'std::string',
            r'std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t>\s*>': 'std::wstring',
            r'std::basic_string<char>': 'std::string',
            r'std::basic_string<wchar_t>': 'std::wstring',
            
            # Vector shorthand
            r'std::vector<(.+?), std::allocator<\1>\s*>': r'std::vector<\1>',
            
            # Map shorthand  
            r'std::map<(.+?), (.+?), std::less<\1>, std::allocator<std::pair<const \1, \2>>\s*>': r'std::map<\1, \2>',
            
            # Unordered map shorthand
            r'std::unordered_map<(.+?), (.+?), std::hash<\1>, std::equal_to<\1>, std::allocator<std::pair<const \1, \2>>\s*>': r'std::unordered_map<\1, \2>',
            
            # Set shorthand
            r'std::set<(.+?), std::less<\1>, std::allocator<\1>\s*>': r'std::set<\1>',
            
            # Shared pointer
            r'std::__shared_ptr<(.+?), __gnu_cxx::_S_atomic>': r'std::shared_ptr<\1>',
            
            # Remove excessive whitespace
            r'\s+': ' ',
            r'\s*<\s*': '<',
            r'\s*>\s*': '>',
            r'\s*,\s*': ', ',
            r'\s*::\s*': '::',
        }
    
    def demangle_type(self, type_string: str) -> str:
        """Apply demangling rules to make type names more readable."""
        result = type_string
        
        for pattern, replacement in self.demangle_rules.items():
            result = re.sub(pattern, replacement, result)
        
        # Clean up any remaining whitespace issues
        result = re.sub(r'\s+', ' ', result).strip()
        
        return result
    
    def format_basic_output(self, 
                          variable_name: str,
                          resolved_type: ResolvedType,
                          position: SourcePosition) -> str:
        """Format basic output as requested in PRD."""
        demangled_type = self.demangle_type(resolved_type.to_string())
        
        output_lines = []
        output_lines.append("🎯 C++ Template Instantiation Analysis")
        output_lines.append("=" * 50)
        output_lines.append("")
        output_lines.append(f"📍 Location: {position.file_path}:{position.line}:{position.column}")
        output_lines.append(f"🏷️  Variable: `{variable_name}`")
        output_lines.append(f"📋 Type: `{demangled_type}`")
        
        if resolved_type.is_template and resolved_type.template_args:
            output_lines.append("")
            output_lines.append("🔍 Template Analysis:")
            output_lines.append(f"  • Base type: {resolved_type.base_type}")
            output_lines.append(f"  • Template arguments ({len(resolved_type.template_args)}):")
            
            for i, arg in enumerate(resolved_type.template_args, 1):
                arg_str = self.demangle_type(arg.to_string())
                output_lines.append(f"    {i}. {arg_str}")
        
        # Add type qualifiers if present
        qualifiers = []
        if resolved_type.is_const:
            qualifiers.append("const")
        if resolved_type.is_pointer:
            qualifiers.append("pointer")
        if resolved_type.is_reference:
            qualifiers.append("reference")
        
        if qualifiers:
            output_lines.append("")
            output_lines.append(f"🔧 Qualifiers: {', '.join(qualifiers)}")
        
        return "\n".join(output_lines)
    
    def format_detailed_output(self,
                             variable_name: str,
                             resolved_type: ResolvedType,
                             position: SourcePosition,
                             context: Dict[str, Any] = None) -> str:
        """Format detailed output with additional context."""
        basic_output = self.format_basic_output(variable_name, resolved_type, position)
        
        output_lines = [basic_output]
        
        if context:
            output_lines.append("")
            output_lines.append("📝 Additional Context:")
            output_lines.append("-" * 30)
            
            # Cursor information
            if 'kind' in context:
                output_lines.append(f"  • AST Node Kind: {context['kind']}")
            
            if 'semantic_parent' in context and context['semantic_parent']:
                output_lines.append(f"  • Semantic Parent: {context['semantic_parent']}")
            
            if 'lexical_parent' in context and context['lexical_parent']:
                output_lines.append(f"  • Lexical Parent: {context['lexical_parent']}")
            
            # Source code context
            if 'source_lines' in context:
                output_lines.append("")
                output_lines.append("📄 Source Context:")
                for line_info in context['source_lines']:
                    marker = ">>> " if line_info['is_target'] else "    "
                    output_lines.append(f"{marker}{line_info['line_number']:4d}: {line_info['content']}")
        
        return "\n".join(output_lines)
    
    def format_json_output(self,
                          variable_name: str,
                          resolved_type: ResolvedType,
                          position: SourcePosition,
                          context: Dict[str, Any] = None) -> str:
        """Format output as JSON for programmatic processing."""
        result = {
            "analysis_timestamp": datetime.now().isoformat(),
            "variable_name": variable_name,
            "location": {
                "file": position.file_path,
                "line": position.line,
                "column": position.column
            },
            "type_analysis": {
                "resolved_type": resolved_type.to_string(),
                "demangled_type": self.demangle_type(resolved_type.to_string()),
                "canonical_name": resolved_type.get_canonical_name(),
                "base_type": resolved_type.base_type,
                "is_template": resolved_type.is_template,
                "is_pointer": resolved_type.is_pointer,
                "is_reference": resolved_type.is_reference,
                "is_const": resolved_type.is_const,
                "namespace": resolved_type.namespace
            }
        }
        
        if resolved_type.is_template and resolved_type.template_args:
            result["type_analysis"]["template_arguments"] = [
                {
                    "index": i,
                    "type": arg.to_string(),
                    "demangled_type": self.demangle_type(arg.to_string()),
                    "is_template": arg.is_template
                }
                for i, arg in enumerate(resolved_type.template_args)
            ]
        
        if context:
            result["context"] = context
        
        return json.dumps(result, indent=2)
    
    def format_error_output(self, error_message: str, position: SourcePosition = None) -> str:
        """Format error messages."""
        output_lines = []
        output_lines.append("❌ TIR Analysis Error")
        output_lines.append("=" * 30)
        output_lines.append("")
        
        if position:
            output_lines.append(f"📍 Location: {position.file_path}:{position.line}:{position.column}")
            output_lines.append("")
        
        output_lines.append(f"🔴 Error: {error_message}")
        
        return "\n".join(output_lines)
    
    def format_no_symbol_found(self, 
                             search_criteria: str,
                             position: SourcePosition = None) -> str:
        """Format output when no symbol is found at the specified location."""
        output_lines = []
        output_lines.append("🔍 TIR Analysis - No Symbol Found")
        output_lines.append("=" * 40)
        output_lines.append("")
        
        if position:
            output_lines.append(f"📍 Search Location: {position.file_path}:{position.line}:{position.column}")
        
        output_lines.append(f"🎯 Search Criteria: {search_criteria}")
        output_lines.append("")
        output_lines.append("💡 Suggestions:")
        output_lines.append("  • Check that the line/column numbers are correct")
        output_lines.append("  • Ensure the cursor is positioned on a variable, parameter, or function")  
        output_lines.append("  • Verify the file can be parsed (check for syntax errors)")
        output_lines.append("  • Try using --compile-commands if analyzing a project file")
        
        return "\n".join(output_lines)
    
    def format_parse_error(self, file_path: str, diagnostics: List[Dict[str, Any]]) -> str:
        """Format parse error information."""
        output_lines = []
        output_lines.append("⚠️  TIR Analysis - Parse Errors")
        output_lines.append("=" * 40)
        output_lines.append("")
        output_lines.append(f"📁 File: {file_path}")
        output_lines.append("")
        
        if diagnostics:
            output_lines.append("🔴 Parse Diagnostics:")
            for i, diag in enumerate(diagnostics, 1):
                severity_map = {0: "Note", 1: "Warning", 2: "Warning", 3: "Error", 4: "Fatal"}
                severity = severity_map.get(diag.get('severity', 3), "Unknown")
                
                location = diag.get('location', {})
                line = location.get('line', '?')
                column = location.get('column', '?')
                
                output_lines.append(f"  {i}. [{severity}] {diag.get('spelling', 'Unknown error')}")
                output_lines.append(f"     Location: line {line}, column {column}")
        else:
            output_lines.append("🔴 Parse failed but no specific diagnostics available")
        
        output_lines.append("")
        output_lines.append("💡 Suggestions:")
        output_lines.append("  • Fix syntax errors in the source file")
        output_lines.append("  • Ensure all required headers are available")
        output_lines.append("  • Use --compile-commands to provide correct include paths")
        
        return "\n".join(output_lines)
    
    def format_summary_output(self, analyses: List[Dict[str, Any]]) -> str:
        """Format summary output for multiple analyses."""
        if not analyses:
            return "No analyses to summarize."
        
        output_lines = []
        output_lines.append("📊 TIR Analysis Summary")
        output_lines.append("=" * 30)
        output_lines.append("")
        output_lines.append(f"Total Analyses: {len(analyses)}")
        
        # Count template vs non-template types
        template_count = sum(1 for a in analyses if a.get('is_template', False))
        output_lines.append(f"Template Types: {template_count}")
        output_lines.append(f"Non-template Types: {len(analyses) - template_count}")
        output_lines.append("")
        
        # List each analysis
        for i, analysis in enumerate(analyses, 1):
            output_lines.append(f"{i}. {analysis.get('variable_name', 'Unknown')}: {analysis.get('demangled_type', 'Unknown')}")
        
        return "\n".join(output_lines)